﻿// Abstract base type for all the forms, contains the fields
// The form widget contains either form or other children widgets
define([
    'Wjs/ui/widgets/foundation/ViewMultipleContainer',
    'Wjs/ui/elements/mixins/Loadable',
    'Wjs/ui/elements/mixins/Maskable',
    'Wjs/ui/widgets/validation/validationSummary',
    'Wjs/ui/widgets/fields/LabeledField',
    'Wjs/ui/messageBox'
],
function () {
    'use strict';

    return Wjs.defineOnce({

        type: 'Form',

        internal: {

            base: Wjs.getType('ViewMultipleContainer'),

            mixins: [
                Wjs.getType('Loadable'),
                Wjs.getType('Maskable')
            ],

            initialize: function (cfg, parent) {

                this.setTag('form');

                this.addClass('wjs-form');

                this.setAttributes({
                    novalidate: true
                });

                this.setDefaultItemType('LabeledField');

                this.setLabelWidth('50%');

                // Validation context (where to store and show the validation errors)
                this.setValidationContext({
                    validationSummary: true,
                    errors: []
                });
            }

        },

        setLabelWidth: function (labelWidth) {

            this.labelWidth = labelWidth;

            return this;
        },

        setValidationContext: function (validationContext) {

            this.validationContext = validationContext;

            return this;
        },

        setEncodingType: function (encodingType) {

            this.setAttribute('enctype', encodingType);

            return this;
        },

        getEncodingType: function () {

            return this.getAttribute('enctype');
        },

        // Handle when the value of a field has changed
        onFieldChanged: function (fcn) {

            this.fieldChangedHandler = fcn;

            return this;
        },

        // Allows extra processing before submitting the data and to cancel the submission by returning false
        onBeforeSubmit: function (fcn) {

            this.beforeSubmitHandler = fcn;

            return this;
        },

        // Called when the form was submitted and returned from the server when in ajax mode
        onSubmit: function (fcn) {

            this.submitHandler = fcn;

            return this;
        },

        // Called when the form was submitted and there was an error when in ajax mode
        onSubmitError: function (fcn) {

            this.submitErrorHandler = fcn;

            return this;
        },

        beforeLoadHandler: function () {
            // Do nothing
        },

        // Called by the loader when there is loaded data
        loadHandler: function (data, contentType) {

            this.getNamedFields().forEach(function (field) { // For each of the fields in the form, set the value using the name
                var name = field.getName(),
                    value = data[name],
                    RadioButton = Wjs.getType('RadioButton', false);

                if (typeof value != 'undefined') { // If null then set the values to null

                    if (field.load) { // It is handling the load event

                        field.load(function () {

                            field.setValue(value, {
                                acceptChanges: true, // Do not set the field as modified
                                fireFieldChanged: false // Do not fire the field change event
                            });
                        });
                    }
                    else {

                        if (RadioButton && field instanceof RadioButton) {

                            if (field.getValue() === value) {

                                field.check();
                            }
                        }
                        else {

                            field.setValue(value, {
                                acceptChanges: true, // Do not set the field as modified
                                fireFieldChanged: false // Do not fire the field change event
                            });
                        }
                        
                    }
                }

            });
        },

        addTextLabeledField: function (text, field) {

            this.addItem(

                Wjs.new('LabeledField')

                    .setTextLabel(text)

                    .setField(field)
            );

            return this;
        },

        addHtmlLabeledField: function (html, field) {

            this.addItem(

                Wjs.new('LabeledField')

                    .setHtmlLabel(html)

                    .setField(field)
            );

            return this;
        },

        addLabeledField: function (label, field) {

            this.addItem(

                Wjs.new('LabeledField')

                    .setLabel(label)

                    .setField(field)
            );

            return this;
        },

        submit: function (bypassBeforeSubmit) {

            if (!bypassBeforeSubmit && this.beforeSubmitHandler) {

                if (this.beforeSubmitHandler() === false) {

                    return false;
                }
            }

            if (!this.validate()) {

                return false;
            }
        },

        // Validates the form
        validate: function () {
            var fields = this.getNamedFields(),
                success = true;

            fields.forEach(function (field) {

                if (!field.validate(this.validationContext)) {

                    success = false;
                }
            },
            this);

            if (!success) { // Show a grid dialog with the errors

                Wjs.validationSummary.setErrors(this.validationContext.errors);
                Wjs.validationSummary.show();

                this.validationContext.errors.length = 0; // Reset the errors
            }

            return success;
        },

        getField: function (name) {
            var fields = this.down(function (item) {

                return (item instanceof Wjs.getType('FieldBase') && item.getName() === name);
            })

            if (fields.length < 1) {

                throw 'Field with name:' + name + ' does not exist in the form';
            }

            if (fields.length > 1) {

                throw 'Expected a single item';
            }

            return fields[0];
        },

        // Returns all the fields of the form that have a name set
        getNamedFields: function () {

            return this.down(function (item) {

                return (item instanceof Wjs.getType('FieldBase') && item.getName());
            })
        },

        // Returns true whether there are modified fields
        isModified: function () {
            var fields = this.getNamedFields(),
                length = fields.length,
                field,
                i;

            for (i = 0; i < length; ++i) {

                field = fields[i];

                if (field.isModified()) {

                    return true; // If one field is modified, the whole form is modified
                }
            }

            return false;
        },

        serialize: function () {

            switch (this.getEncodingType()) {

                case 'multipart/form-data': return new FormData(this.domEl);

                case 'application/x-www-form-urlencoded': return this.serializeParameters(this.getRecord());

                default: return JSON.stringify(this.getRecord());
            }
        },

        // Serializes the parameters of the record in form url encoded
        serializeParameters: function (record) {
            var parts = [],
                url;

            for (var key in record) {

                parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(record[key]));
            }

            url = parts.join('&');

            if (this.method == 'get') {

                return '?' + url;
            }

            return url;
        },
        // Returns an object with the data of the form fields indexed by the name of the field
        getRecord: function () {
            var fields = this.getNamedFields(),
                record = {},
                RadioButton = Wjs.getType('RadioButton', false),
                radioButtons = {}; // Track the state of the radio buttons

            fields.forEach(function (field) {
                var fieldName = field.getName();

                if (RadioButton && field instanceof RadioButton) { // Radio buttons need to send their values when checked

                    if (field.isChecked()) {

                        record[fieldName] = field.getValue();

                        radioButtons[fieldName] = true; // Checked
                    }
                    else {

                        if (!radioButtons[fieldName]) { // Not checked already

                            radioButtons[fieldName] = false; // Un-checked
                        }
                    }
                }
                else { // Not a radio button

                    record[fieldName] = field.getValue();
                }         
            });

            // Add the un-checked radio buttons as false value
            for (var name in radioButtons) {

                if (!radioButtons[name]) { // Un-checked

                    record[name] = false;
                }
            }

            return record;
        },

        acceptChanges: function () {
            var me = this,
                fields = me.getNamedFields(),
                length = fields.length,
                field,
                i;

            for (i = 0; i < length; ++i) {

                field = fields[i];

                field.acceptChanges();
            }
        },

    });

});